#include <strmif.h>
#include <string>
#include "mkvmuxfilter.hpp"
#include <vfwmsgs.h>
#include <cassert>
//#include "mediatypeutil.hpp"
//#include <uuids.h>

namespace MkvMux
{

Pin::Pin(
    Filter* pFilter,
    const wchar_t* id,
    PIN_DIRECTION d) :
    m_pFilter(pFilter),
    m_id(id),
    m_dir(d),
    m_connection(0)
{
}


Pin::~Pin()
{
    assert(m_connection == 0);
    //assert(m_connection_mt.Empty());
}


HRESULT Pin::EnumMediaTypes(IEnumMediaTypes** pp)
{
    Filter::Lock lock;
    
    HRESULT hr = lock.Seize(m_pFilter);
    
    if (FAILED(hr))
        return hr;
    
    return m_preferred.CreateEnum(this, pp);
}


HRESULT Pin::Disconnect()
{
    Filter::Lock lock;
    
    HRESULT hr = lock.Seize(m_pFilter);
    
    if (FAILED(hr))
        return hr;
    
    if (m_pFilter->m_state != State_Stopped)
        return VFW_E_NOT_STOPPED;
        
    m_connection_mt.Clear();
    
    if (m_connection == 0)
        return S_FALSE;
        
    OnDisconnect();
        
    m_connection->Release();
    m_connection = 0;
    
    //TODO: there's probably work that needs to be done here
    
    return S_OK;
}


HRESULT Pin::ConnectedTo(IPin** pp)
{
    if (pp == 0)
        return E_POINTER;
        
    IPin*& p = *pp;
    
    Filter::Lock lock;
    
    HRESULT hr = lock.Seize(m_pFilter);
    
    if (FAILED(hr))
        return hr;

    p = m_connection;
    
    if (p == 0)
        return VFW_E_NOT_CONNECTED;
        
    p->AddRef();
    return S_OK;
}
        
    
HRESULT Pin::ConnectionMediaType(AM_MEDIA_TYPE* p)
{
    if (p == 0)
        return E_POINTER;
        
    Filter::Lock lock;
    
    HRESULT hr = lock.Seize(m_pFilter);
    
    if (FAILED(hr))
        return hr;
    
    if (m_connection == 0)
        return VFW_E_NOT_CONNECTED;
        
    assert(m_connection_mt.Size() == 1);        

    return m_connection_mt.Copy(0, *p);
}


HRESULT Pin::QueryPinInfo(PIN_INFO* p)
{
    if (p == 0)
        return E_POINTER;
        
    Filter::Lock lock;
    
    HRESULT hr = lock.Seize(m_pFilter);
    
    if (FAILED(hr))
        return hr;
    
    PIN_INFO& i = *p;
    
    i.pFilter = static_cast<IBaseFilter*>(m_pFilter);
    i.pFilter->AddRef();
    
    i.dir = m_dir;
    
    enum { buflen = sizeof(i.achName)/sizeof(WCHAR) };

    const errno_t e = wcscpy_s(i.achName, buflen, m_id);
    assert(e == 0);
    e;

    return S_OK;
}


HRESULT Pin::QueryDirection(PIN_DIRECTION* p)
{
    if (p == 0)
        return E_POINTER;
        
    *p = m_dir;
    return S_OK;
}

    
HRESULT Pin::QueryId(LPWSTR* p)
{
    if (p == 0)
        return E_POINTER;
        
    wchar_t*& id = *p;
    
    const size_t len = wcslen(m_id);             //wchar strlen
    const size_t buflen = len + 1;               //wchar strlen + wchar null
    const size_t cb = buflen * sizeof(wchar_t);  //total bytes

    id = (wchar_t*)CoTaskMemAlloc(cb);    
    
    if (id == 0)
        return E_OUTOFMEMORY;
    
    const errno_t e = wcscpy_s(id, buflen, m_id);
    assert(e == 0);
    e;

    return S_OK;
}


HRESULT Pin::OnDisconnect()
{
    return S_OK;
}


} //end namespace MkvMux

